﻿using FireChat.Server.Session.Receiver;
using FireChat.Server.Session.Remote;
using FireChat.Share.Logger;
using System.Net;
using System.Net.WebSockets;

namespace FireChat.Server.Session;



/// <summary>
/// FireChat 服务端核心
/// </summary>
public class ServerCore
{
    /// <summary>
    /// 服务器核心
    /// </summary>
    private readonly HttpListener _listener = new HttpListener();

    /// <summary>
    /// 已连接的远程客户端列表
    /// </summary>
    private List<RemoteSession> _remoteSessions { get; } = new();



    /// <summary>
    /// 本地绑定的地址
    /// </summary>
    public Uri LocalUri { get; }

    /// <summary>
    /// 接收到的远程客户端接收器
    /// </summary>
    public event Action<PackageReceiver>? RemoteClientReceive;

    /// <summary>
    /// 所有远程客户端
    /// </summary>
    public IEnumerable<RemoteSession> RemoteSessions => _remoteSessions;



    /// <summary>
    /// 初始化服务端
    /// </summary>
    /// <param name="url">要绑定的地址</param>
    public ServerCore(string url)
    {
        _listener.Prefixes.Add(url);
        LocalUri = new(url);
    }



    /// <summary>
    /// 启动服务端
    /// </summary>
    public Task LaunchAsync()
    {
        _listener.Start();

        _ = Task.Run(() =>
        {
            $"FireChat 服务端核心 已启动 [{LocalUri}]".LogToConsole();

            AcceptRemoteClient(_listener, session => InitRemoteSession(session));
        });

        return Task.CompletedTask;
    }

    /// <summary>
    /// 关闭服务器
    /// </summary>
    public void Close()
    {
        _listener.Close();
    }


    /// <summary>
    /// 初始化远程连接
    /// </summary>
    private void InitRemoteSession(RemoteSession session)
    {
        _remoteSessions.Add(session);

        session.PackageReceived += package =>
        {
            RemoteClientReceive?.Invoke(new() { Session = session, Package = package });
        };

        session.Closing += () =>
        {
            _remoteSessions.Remove(session);
        };

        session.ReceiveAsync();
    }

    /// <summary>
    /// 等待远程客户端连接
    /// </summary>
    /// <param name="listener">服务器监听器</param>
    /// <param name="acceptClientCallback">客户端连接回调方法</param>
    private static async void AcceptRemoteClient(HttpListener listener, Action<RemoteSession> acceptClientCallback)
    {
        while (listener.IsListening)
        {
            try
            {
                HttpListenerContext context = await listener.GetContextAsync();

                if (context.Request.IsWebSocketRequest)
                {
                    HttpListenerWebSocketContext webSocketContext = await context.AcceptWebSocketAsync(null);
                    acceptClientCallback?.Invoke(new(webSocketContext));
                }
                else
                {
                    context.Response.StatusCode = 400;
                    context.Response.Close();
                }
            }
            catch
            {
                return;
            }
        }
    }
}
